/*
    Meter JMX Utility Library
    Copyright (C) 2005  Noah Campbell

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
package info.noahcampbell.meter;


import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author Noah Campbell
 * @version 1.0
 * @param <T> metric <code>Metric</code> subclass.
 * @param <V> object
 */
public abstract class MetricObserver<T extends Metric, V> implements Runnable {
    
    /**
     * Construct a new <code>MetricObserver</code>.
     *
     * @param c
     */
    protected MetricObserver(Collection<T> c) {
        if(c.size() <= 0) throw new IllegalArgumentException("Please provide at least one Meter to observe");
        for(T t : c) {
            metrics.put(t, newInstance());
        }
        executor.scheduleAtFixedRate(this, 2500, 500, TimeUnit.MILLISECONDS);

    }
    
    /**
     * Construct a new <code>MetricObserver</code>.
     *
     * @param ts
     */
    protected MetricObserver(T...ts) {
        this(Arrays.asList(ts));
    }
    
    
    /** The metrics. */
    private Map<T,V> metrics = new HashMap<T, V>();
    
    /** The executor. */
    protected ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);

    
    /**
     * @param number
     * @param obj
     */
    protected abstract void process(Number number, V obj);
    
    
    /**
     * Return a new instance of V.  
     * 
     * <b>Note</b> This method is used in the constructor
     * of MetricObserver so you cannot access any field values of the deriving
     * class.
     * 
     * @return instance
     */
    protected abstract V newInstance();
    
    /**
     * @see java.lang.Runnable#run()
     */
    public void run() {
        
        for(Map.Entry<T, V> entry : this.metrics.entrySet()) {
        
            if(entry.getKey() == null) {
                // log that a null snuck in.
                continue;
                
            }
            try {
                Queue<Number> queue = entry.getKey().getStatisticsQueue();
                V s = entry.getValue();
                while(queue.peek() != null) {
                    Number poll = queue.poll();
                    if(poll == null) { 
                        break;
                    }
                    process(poll, s);
                } 
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                
            }
        }
        
        return;
    }
    
    public void addMetric(T metric) {
        metrics.put(metric, newInstance());
    }
    
    public V getStats(T metric) {
        return metrics.get(metric);
    }
}
